﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.Extensions.DependencyInjection;

namespace Fast.Extensions.DatabaseAccessor.EntityFrameworkCore
{
    /// <summary>
    /// 数据库上下文
    /// </summary>
    /// <typeparam name="TDbContext"></typeparam>
    [SkipScan]
    public abstract class FastDbContext<TDbContext> : FastDbContext<TDbContext, DefaultDbContextLocator>
        where TDbContext : DbContext
    {
        /// <summary>
        /// ctor
        /// </summary>
        public FastDbContext(DbContextOptions<TDbContext> options) : base(options)
        {

        }
    }
    /// <summary>
    /// 数据库上下文
    /// </summary>
    /// <typeparam name="TDbContext"></typeparam>
    [SkipScan]
    public abstract class FastDbContext<TDbContext, TDbContextLocator> : DbContext
        where TDbContext : DbContext
        where TDbContextLocator : class, IDbContextLocator
    {
        private static ConcurrentDictionary<string, string> _tenantDic = new ConcurrentDictionary<string, string>();
        /// <summary>
        /// ctor
        /// </summary>
        public FastDbContext(DbContextOptions<TDbContext> options) : base(options)
        {

        }

        /// <summary>
        /// 当前租户
        /// </summary>
        public virtual Tenant CurrentTenant
        {
            get
            {
                var httpContext = App.HttpContext;
                if (httpContext == null) return default;

                var tenant = httpContext.User?.FindFirst(TenantConsts.TENANT_KEY);

                if (!int.TryParse(tenant?.Value, out var tenantId)) return default;

                var serviceProvider = httpContext.RequestServices;

                EFCoreTenant currentTenant;

                var tenantKey = $"MULTI_TENANCY_{tenantId}";
                if (!_tenantDic.TryGetValue(tenantKey, out var tenantInfo))
                {
                    var dbContextResolve = serviceProvider.GetService<Func<Type, DbContext>>();
                    if (dbContextResolve == null) return default;

                    var tenantDbContext = dbContextResolve.Invoke(typeof(MultiTenancyDbContextLocator));

                    currentTenant = tenantDbContext.Set<EFCoreTenant>().AsNoTracking().FirstOrDefault(p => p.TenantId == tenantId);
                    if (currentTenant != null)
                    {
                        var tenantJson = Newtonsoft.Json.JsonConvert.SerializeObject(currentTenant);
                        _tenantDic.TryAdd(tenantKey, tenantJson);
                    }
                }
                else
                {
                    currentTenant = Newtonsoft.Json.JsonConvert.DeserializeObject<EFCoreTenant>(tenantInfo);
                }

                return currentTenant;
            }
        }

        /// <summary>
        /// 数据库上下文配置模型调用方法
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            //配置数据库上下文实体
            AppDbContextBuilder.ConfigureDbContextEntity(modelBuilder, this, typeof(TDbContextLocator));
        }
        /// <summary>
        /// 在调用DbContext.SaveChanges之前调用
        /// </summary>
        /// <param name="eventData"></param>
        /// <param name="result"></param>
        protected virtual void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        {

        }
        /// <summary>
        /// 在调用DbContext.SaveChanges之后调用
        /// </summary>
        /// <param name="eventData"></param>
        /// <param name="result"></param>
        protected virtual void SavedChangesEvent(SaveChangesCompletedEventData eventData, int result)
        {

        }
        /// <summary>
        /// 在调用DbContext.SaveChanges失败之后调用
        /// </summary>
        /// <param name="eventData"></param>
        protected virtual void SaveChangesFailedEvent(DbContextErrorEventData eventData)
        {

        }

        #region 内部调用
        internal void SaveChangesBefore(DbContextEventData eventData, InterceptionResult<int> result)
        {
            SavingChangesEvent(eventData, result);
        }
        internal void SaveChangesAfter(SaveChangesCompletedEventData eventData, int result)
        {
            SavedChangesEvent(eventData, result);
        }
        internal void SaveChangesFailedAfter(DbContextErrorEventData eventData)
        {
            SaveChangesFailedEvent(eventData);
        }
        #endregion

        /// <summary>
        /// 软删除查询筛选器表达式
        /// </summary>
        /// <param name="entityTypeBuilder"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        protected virtual LambdaExpression SoftDeleteQueryFilterExpression(EntityTypeBuilder entityTypeBuilder, string propertyName)
        {
            var clrType = entityTypeBuilder.Metadata.ClrType;
            if (clrType.GetProperty(propertyName) == null) return default;

            //创建参数t
            var parameter = Expression.Parameter(clrType, "t");
            var propertyNameExp = Expression.Property(parameter, propertyName);
            var propertyValueExp = Expression.Constant(false);
            // 构建表达式 t=>t.IsDeleted == false
            var body = Expression.Equal(propertyNameExp, propertyValueExp);
            return Expression.Lambda(body, parameter);

        }

        /// <summary>
        /// 基于表租户查询过滤器表达式
        /// </summary>
        /// <param name="entityTypeBuilder">实体类型构建器</param>
        /// <param name="dbContext"></param>
        /// <returns>表达式</returns>
        protected virtual LambdaExpression TenantQueryFilterExpression(EntityTypeBuilder entityTypeBuilder, DbContext dbContext)
        {
            var onTableTenantId = nameof(TenantEntity.TenantId);

            var clrType = entityTypeBuilder.Metadata.ClrType;
            if (clrType.GetProperty(onTableTenantId) == null) return default;

            // 创建表达式元素
            var parameter = Expression.Parameter(clrType, "t");
            var propertyName = Expression.Constant(onTableTenantId);
            var propertyValue = Expression.Call(Expression.Constant(dbContext), dbContext.GetType().GetMethod(nameof(IMultiTenancyOnTable.GetCurrentTenantId)));
            // 构建表达式  EF.Property<object>(t=>t.TenantId==dbContext.GetCurrentTenantId())
            var expressionBody = Expression.Equal(Expression.Call(typeof(EF), nameof(EF.Property), new[] { typeof(object) }, parameter, propertyName), propertyValue);
            var expression = Expression.Lambda(expressionBody, parameter);
            return expression;
        }
    }
}
